package cn.batch.daemon.loadBatch;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPFileFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.batch.daemon.Configuration;
import cn.batch.daemon.bean.TaskMissionBean;
import cn.batch.daemon.hibernate.bean.BatchRiskData;
import cn.batch.daemon.hibernate.bean.PbocErrorCust;
import cn.batch.daemon.hibernate.bean.PbocSummary;
import cn.batch.daemon.jdbc.MessionTaskStore;
import cn.batch.daemon.service.IProcReportService;
import cn.batch.daemon.service.RiskWarningService;
import cn.batch.daemon.taskEnum.MissionSts;
import cn.batch.daemon.taskEnum.TaskMissionType;
import cn.batch.daemon.util.Assert;
import cn.batch.daemon.util.BuildPbocSummary;
import cn.batch.daemon.util.ConstantParameter;
import cn.batch.daemon.util.DateUtil;
import cn.batch.daemon.util.FtpUtil;
import cn.batch.daemon.util.UnZipFile;

/**
 * 按月加载，只加载文件名带eds的zip文件。
 * @author MyUser
 *
 */
public class RiskWarnMonthLoadProcReport {
	private static final Log logger = LogFactory
			.getLog(RiskWarnMonthLoadProcReport.class);

	private String localPath;
	private String edsOutFilePath; // 解压缩文件的指定目录
	private String errorFilePath; // 解析出错的文件存放目录
	private String transEndFlag; // 文件传输完毕标志
	private long threadRunningTime = 0;
	private int errorSleepTime = 300000;

	// FTP 配置
	private String ip;
	private int port;
	private String username;
	private String password;
	private String remotePath;

	private String batchDate = null;
	private boolean initialized = false;
	private int totalNumber = 0; // 解析文件总数
	private int errorNumber = 0; // 解析出错文件数

	private Configuration config;
	private ApplicationContext ctx;
	private IProcReportService procReportService;
	private RiskWarningService riskWarningService;

	public RiskWarnMonthLoadProcReport(Configuration config) {
		super();
		this.config = config;
	}

	public void run() {
		initialize();
		Assert.state(initialized, "必须初始化完成才能运行。");

		boolean isDownloadSuccess = false;
		long threadStartupTime = System.currentTimeMillis();
		TaskMissionBean tmb = new TaskMissionBean();
		try {
			// 0、更新任务状态表，状态 执行中
			tmb.setBatch_no(batchDate);
			tmb.setMissionTyp(TaskMissionType.PRCLM);
			tmb.setDisposeStartTm(new Timestamp(System.currentTimeMillis())); // 起始时间
			tmb.setMissionSts(MissionSts.DISPOSING); // 任务状态
			tmb.setRemark("开始装载人行征信数据，请等待。"); // 备注
			MessionTaskStore.updateTaskMissionStatus(tmb);

			// 1、下载人行征信文件
			while (true) {
				try {
					isDownloadSuccess = FtpUtil.downloadFile(ip, port, username,
							password, remotePath, localPath, transEndFlag,
							new FTPFileFilter() {

								@Override
								public boolean accept(FTPFile arg0) {
									String fname = arg0.getName().toLowerCase();
									return fname.contains("eds")
											|| fname.endsWith(".txt");
								}
							});
				} catch (Exception e) {
					logger.error(e);
				}

				if (!isDownloadSuccess) {
					if (System.currentTimeMillis() - threadStartupTime < threadRunningTime) {
						sleep(errorSleepTime);
						logger.warn("线程休眠后，继续运行。");
						continue;
					} else {
						throw new RuntimeException("未获取到人行征信文件。");
					}
				} else {
					break;
				}
			}

			// 2、处理人行征信文件
			handleDir(localPath);

			// 如果有解析失败的文件，则将任务状态置为失败
			if (errorNumber > 0) {
				throw new RuntimeException("解析文件总数：" + totalNumber +"失败：" + errorNumber);
			}
			
			// 3、更新任务状态表，状态 结束
			tmb.setDisposeEndTm(new Timestamp(System.currentTimeMillis()));
			tmb.setMissionSts(MissionSts.SUCCESS); // 任务状态
			String s = "结束装载征信数据。文件总数：" + totalNumber + "，成功："
					+ (totalNumber - errorNumber) + "，失败：" + errorNumber;
			tmb.setRemark(s); // 备注
			MessionTaskStore.updateTaskMissionStatus(tmb);

			// log
			logger.info(s);
		} catch (Exception e) {
			// 更新任务状态表 ，状态 失败
			tmb.setDisposeEndTm(new Timestamp(System.currentTimeMillis()));
			tmb.setMissionSts(MissionSts.FAILED);
			tmb.setRemark("按月装载征信数据出错，请联系系统管理员解决问题。" + e.getMessage());
			try {
				MessionTaskStore.updateTaskMissionStatus(tmb);
			} catch (SQLException sqle) {
				logger.error("更新任务状态或标识表失败。", sqle);
			}
			// log
			logger.error("按月装载征信数据出错。", e);
			throw new RuntimeException(e);
		}

	} // method end

	private void initialize() {
		if (ctx == null) {
			ctx = new ClassPathXmlApplicationContext(
					new String[] { "applicationContext*.xml" });
		}

		procReportService = (IProcReportService) ctx
				.getBean("procReportService");

		riskWarningService = (RiskWarningService) ctx
				.getBean("riskWarningService");

		errorSleepTime = config.getSetting(ConstantParameter.ERROR_SLEEPTIME,
				ConstantParameter.DEFAULT_SLEEPTIME);
		threadRunningTime = config.getSetting(
				ConstantParameter.THREAD_RUNNING_TIME, 0);

		localPath = config.getSetting(ConstantParameter.SEARCH_URL, "").trim();
		edsOutFilePath = config.getSetting(ConstantParameter.EDS_OUT_FILE_URL, "")
				.trim();
		errorFilePath = config.getSetting(ConstantParameter.ERROR_FILE_URL, "")
				.trim();

		transEndFlag = config.getSetting(ConstantParameter.TRANSFER_END_Flag,
				"endFlag").trim();

		// INIT FTP
		ip = config.getSetting(ConstantParameter.PROC_FTP_IP, "").trim();
		port = config.getSetting(ConstantParameter.PROC_FTP_PORT, 21);
		username = config.getSetting(ConstantParameter.PROC_FTP_USERNAME, "")
				.trim();
		password = config.getSetting(ConstantParameter.PROC_FTP_PASSWORD, "")
				.trim();
		remotePath = config.getSetting(ConstantParameter.PROC_FTP_REMOTEPATH,
				"").trim();

//		if (batchDate == null) {
//			throw new RuntimeException("请传入批次日期。格式：yyyyMMdd");
//		}
		remotePath += batchDate + File.separator;
		localPath += batchDate + File.separator;
		edsOutFilePath += batchDate + File.separator;
		errorFilePath += batchDate + File.separator;

		initialized = true;

	}

	private void handleDir(String localPath2) {
		File file = new File(localPath2);
		File[] files = file.listFiles();
		for (File f : files) {
			try {
				loadProcFile(f);
			} catch (Exception e) {
				logger.error("解析人行征信文件出错。文件名：" + f.getName(), e);
				// 继续处理下一个文件
			}
		}
	}

	private void loadProcFile(File f) throws IOException {
		String filename = f.getName().toLowerCase();
		if (filename.endsWith(".zip") && filename.contains("eds")) {
			handleZip(f);
		} else if (filename.endsWith(".txt")) {
			handleTxt(f);
		}
	}

	private void handleTxt(File f) throws IOException {
		String filename = f.getName().toLowerCase();
		logger.info("开始处理文件：" + filename);
		BufferedReader bufferReader = null;
		try {
			bufferReader = new BufferedReader(new InputStreamReader(
					new FileInputStream(f), "GBK"));

			// 解析错误数据文件,存储至数据库
			List<PbocErrorCust> errorList = new ArrayList<PbocErrorCust>();
			String line;
			int count = 0;
			while ((line = bufferReader.readLine()) != null) {
				try {
					count++;
					PbocErrorCust pe = new PbocErrorCust();
					String[] row = line.split(",");
					if (row.length < 4) {
						logger.error("解析人行征信处理失败数据文件第 " + count
								+ " 行时出错。文件字段或格式不符。" + line);
						continue;
					}
					// pe.setQueryDate(batchDate);
					pe.setQuerierName(row[0]);
					pe.setQuerierCertType(row[1]);
					pe.setQuerierCertNo(row[2]);
					pe.setErrorReason(row[3]);
					pe.setBatchDate(DateUtil.fromString(batchDate, "yyyyMMdd"));
					pe.setBatchType(TaskMissionType.FXYJM.getValue());
					if (row.length > 4) {
						pe.setStandby1(row[4]);
					}
					if (row.length > 5) {
						pe.setStandby2(row[5]);
					}
					errorList.add(pe);
				} catch (Exception e) {
					logger.error(
							"解析人行征信处理失败数据文件第 " + count + " 行时出错。文件名: "
									+ f.getName(), e);
				}
			}
			// 入库
			saveErrorCustInfo(errorList);
		} finally {
			try {
				if (bufferReader != null) {
					bufferReader.close();
				}
			} catch (IOException ioe) {
				logger.error(ioe);
			}
		}
		logger.info("结束处理文件：" + filename);
	}

	private void handleZip(File f) throws IOException {
		String filename = f.getName().toLowerCase();
		logger.info("开始处理文件：" + filename);
		//解压之前先清理edsOutFilePath 目录
		File[] fclear = new File(edsOutFilePath).listFiles();
		if(fclear!=null){
			for(int i =0;i<fclear.length;i++){
				fclear[i].delete();
			}
		}
		
		
		// 解压缩文件到指定目录
		UnZipFile.unZipFiles(f, edsOutFilePath);
		// 处理解压后的文件
		File[] xmlFiles = new File(edsOutFilePath).listFiles(new FileFilter() {

			@Override
			public boolean accept(File pathname) {
				return pathname.getName().endsWith(".xml");
			}
		});

		List<File> errorFileList = new ArrayList<File>();
		for (File file : xmlFiles) {
			try {
				// 入库
				//增加参数fileName(解析文件名称)解决空征信报告问题 HLRain 20130829
				savePbocSummary(file,file.getName());
			} catch (Exception e) {
				logger.error("解析人行征信XML文件时出错。文件名: " + file.getName(), e);
				errorFileList.add(file);
			}
		}
		// 保存错误文件
		if (errorFileList.size() > 0) {
			saveErrorFile(errorFileList, f.getName());
		}
		totalNumber += xmlFiles.length;
		errorNumber += errorFileList.size();
		logger.info("结束处理文件：" + filename);
	}

	private void saveErrorCustInfo(List<PbocErrorCust> erList) {
		procReportService.saveErrorCustInfo(erList);
	}

	private void saveErrorFile(List<File> errorFileList, String zipName) {
		File errorFileDir = new File(errorFilePath);
		if (!errorFileDir.exists()) {
			errorFileDir.mkdirs();
		}
		try {
//			ZipFile.ZipFiles(new File(errorFilePath + "ParseError_" + zipName), "",
//					errorFileList.toArray(new File[errorFileList.size()]));
			for (File f : errorFileList) {
				String name = f.getName();
				int index = name.lastIndexOf("\\");
				if (index > 0) {
					name = name.substring(index + 1);
				}
				FileOutputStream fos = new FileOutputStream(errorFilePath + "eds_" + name);
				FileInputStream fis = new FileInputStream(f);
				
				byte[] b = new byte[4096];
				int length = 0;
				while ((length = fis.read(b)) != -1) {
					fos.write(b, 0, length);
				}
				fis.close();
				fos.close();
			}
		} catch (Exception e) {
			logger.error("保存解析出错文件异常。", e);
		}
	}

	private void sleep(long millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			logger.warn("Thread sleep fail: ", e);
		}
	}

	private void savePbocSummary(File xmlFile,String fileName) throws Exception {

		PbocSummary pbocSummary = BuildPbocSummary.bulidPbocSummary(xmlFile, batchDate,fileName);

			PbocSummary qp = new PbocSummary();
			qp.setName(pbocSummary.getName());
			qp.setCertno(pbocSummary.getCertno());
			qp.setCerttype(pbocSummary.getCerttype());
			List<PbocSummary> list = procReportService.queryPbocSummary(qp);
			if (list.size() > 0) {
				for (PbocSummary pbocSummary2 : list) {
					procReportService.deletePbocSummary(pbocSummary2);
					logger.info(new StringBuilder("Delete Repeating Data: ")
					.append(pbocSummary2.getQuerytime()).append(", ")
					.append(pbocSummary2.getName()).append(", ")
					.append(pbocSummary2.getCertno()).append(", ")
					.append(pbocSummary2.getCerttype())
					.toString());
				}
//				PbocSummary py = (PbocSummary) list.get(0);
//				procReportService.deletePbocSummary(py);
			}
			procReportService.savePbocSummary(pbocSummary);
			
			// 更新待处理客户数据征信装载标志
			BatchRiskData batchRiskData = new BatchRiskData();
			batchRiskData.setPaperid(pbocSummary.getCertno());
			batchRiskData.setPaperkind(pbocSummary.getCerttype());
			batchRiskData.setBatchNo(batchDate);
			batchRiskData.setProcFlag("0");
			riskWarningService.updateBatchRiskData(batchRiskData);
	}

	public void setBatchDate(String batchDate) {
		this.batchDate = batchDate;
	}

}
